#ifndef OBSTACLEMANAGER_H
#define OBSTACLEMANAGER_H

#include <vector>

#include "ngl/Transformation.h"
#include "ngl/TransformStack.h"
#include "ngl/Obj.h"
#include "ngl/Vector.h"

#include "ShaderManager.h"

/// @file ObstacleManager.h
/// @brief manages the obstacles and their interaction
/// @author Perseedoss Rajiv
/// @version 1.0
/// @date 12.04.11
/// Revision History :
/// Initial Version 12.04.11
/// @class ObstacleManager
/// @brief the manager of obstacle, responsible for creating and maintaining them

class ObstacleManager
{
public:
    /// @brief ctor
    ObstacleManager();

    /// @brief dtor
    ~ObstacleManager();

    /// @brief load vbo geometry
    void loadGeometry();

    /// @brief draws the obstacles
    /// @param[in] _txStack the transform stack, used for pushing and popping opengl states
    /// @param[in,out] io_sman the shader manager, used for shading/rendering of the objects
    /// @param[in] _shader the shader used for drawing
    void draw
            (
                ngl::TransformStack _txStack,
                ShaderManager* io_sman,
                const std::string _shader
            );

    /// @brief returns whether collision occured with a particle
    /// @param[in] _currentPosition the current position of the Particle
    /// @param[in] _lastPosition the last position of the Particle
    /// @param[in] _currentParticleRadius the radius of the Particle
    /// @param[out] o_newPosition the safe position outside of the obstacle
    bool checkAndResolveCollision
                        (
                            const ngl::Vector _currentPosition,
                            const ngl::Vector _lastPosition,
                            const float _currentParticleRadius,
                            ngl::Vector &o_newPosition
                        );

    /// @brief sets the toggle flag for obstacle
    /// @param[in] _v the toggle for obstacles
    inline void setObstacleEnabled(const bool _v) { m_obstacleEnabled = _v; }

    /// @brief returns the toggle flag of the obstacles
    inline bool getObstacleEnabled() { return m_obstacleEnabled; }

    /// @brief sets the toggle flag for cube obstacle
    /// @param[in] _v the toggle for cube obstacle
    inline void setCubeEnabled(const bool _v) { m_cubeEnabled = _v; }

    /// @brief returns the toggle flag of the cube obstacle
    inline bool getCubeEnabled() { return m_cubeEnabled; }

    /// @brief sets the toggle flag for bbox drawing for cube obstacle
    /// @param[in] _v the toggle for bbox drawing for cube obstacle
    inline void setDrawBBox(const bool _v) { m_drawBBox = _v; }

    /// @brief returns the toggle flag of the bbox drawing for the cube obstacle
    inline bool getDrawBBox() { return m_drawBBox; }

    /// @brief sets the position of the cube obstacle
    /// @param[in] _v the position of the cube obstacle
    inline void setCubePosition(ngl::Vector _v)
    {
        m_cubePosition = _v;
        m_cubeBBox->setCenter(_v);
    }

    /// @brief returns the position of the cube obstacle
    inline ngl::Vector getCubePosition() { return m_cubePosition; }

    /// @brief sets the size of the cube obstacle
    /// @param[in] _v the size of the cube obstacle
    inline void setCubeDimension(const ngl::Vector _v)
    {
        m_cubeDimension = _v;
        delete m_cubeBBox;
        m_cubeBBox = new ngl::BBox(m_cubePosition, m_cubeDimension.m_x * 1.1, m_cubeDimension.m_y * 1.1, m_cubeDimension.m_z * 1.1);
    }

    /// @brief returns the size of the cube obstacle
    inline ngl::Vector getCubeDimension() { return m_cubeDimension; }

    /// @brief sets the toggle flag for sphere obstacle
    /// @param[in] _v the toggle for sphere obstacle
    inline void setSphereEnabled(const bool _v) { m_sphereEnabled = _v; }

    /// @brief returns the toggle flag of the sphere obstacle
    inline bool getSphereEnabled() { return m_sphereEnabled; }

    /// @brief sets the position of the sphere obstacle
    /// @param[in] _v the position of the sphere obstacle
    inline void setSpherePosition(const ngl::Vector _v) { m_spherePosition = _v; }

    /// @brief returns the position of the sphere obstacle
    inline ngl::Vector getSpherePosition() { return m_spherePosition; }

    /// @brief sets the radius of the sphere obstacle
    /// @param[in] _v the radius of the sphere obstacle
    inline void setSphereRadius(const int _v) { m_sphereRadius = _v; }

    /// @brief returns the radius of the sphere obstacle
    inline int getSphereRadius() { return m_sphereRadius; }

private:
    /// @brief toggle flag for obstacles
    bool m_obstacleEnabled;

    /// @brief toggle flag for sphere obstacle
    bool m_sphereEnabled;

    /// @brief position of sphere obstacle
    ngl::Vector m_spherePosition;

    /// @brief radius of sphere obstacle
    int m_sphereRadius;

    /// @brief toggle flag for cube obstacle
    bool m_cubeEnabled;

    /// @brief toggle flag for bbox drawing
    bool m_drawBBox;

    /// @brief size of cube obstacle
    ngl::Vector m_cubeDimension;

    /// @brief position of cube obstacle
    ngl::Vector m_cubePosition;

    /// @brief bounding box of cube obstacle
    ngl::BBox* m_cubeBBox;

private:
    /// @brief determines collision between 2 spheres and returns a safe position outside of the obstacle
    /// @param[in] _obstacleCenter the center position of the obstacle
    /// @param[in] _obstacleRadius the radius of the obstacle
    /// @param[in] _objectCenter the center position of the collided object (Particle)
    /// @param[in] _objectRadius the radius of the collided object (Particle)
    /// @param[out] o_safePosition the safe position outside of the obstacle
    bool isObjectInsideSphere
                    (
                        const ngl::Vector _obstacleCenter,
                        const float _obstacleRadius,
                        const ngl::Vector _objectCenter,
                        const float _objectRadius,
                        ngl::Vector &o_safePosition
                    );

    /// @brief determines collision between sphere and cube and returns a safe position outside of the obstacle
    /// @param[in] _cubeBBox the bbox of the obstacle cube
    /// @param[in] _currentPosition the current position of the Particle
    /// @param[in] _lastPosition the last position of the Particle
    /// @param[out] o_newPosition the safe position outside of the obstacle
    bool isObjectInsideCube
                    (
                        ngl::BBox* _cubeBBox,
                        const ngl::Vector _currentPosition,
                        const ngl::Vector _lastPosition,
                        ngl::Vector &o_newPosition
                    );
};

#endif // OBSTACLEMANAGER_H
